/*
 * inotify-sync - a simple file synchronizer and file system watcher
 * Copyright (C) 2010-2011, inotify-sync developers and inotify-sync contributors
 * Copyright (C) 2010-2011, Cohesion Network Security Studio
 *
 * inotify-sync is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * inotify-sync is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with inotify-sync; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

/*
 * FILE:		XmlLogger.cpp
 * USAGE:		---
 * DESCRIPTION:		---
 * OPTIONS:		---
 * REQUIREMENTS:	---
 * BUGS:		---
 * NOTES:		---
 * AUTHOR:		LI Yiwei (mail:leeyiw@gmail.com)
 * COMPANY:		---
 * VERSION:		1.0
 * CREATED:		Thu Sep  1 13:55:46 CST 2011
 * REVISION:		---
 */

#include "XmlLogger.h"

XmlLogger::XmlLogger(const Profile& profile)
try
{
	this->profile=profile;
/*检测是否存在log文件
 *如果不存在则创建
 *如果存在则查看是否具有读写权限
 *如果无读或写权限或者不能创建
 *则直接报错退出
 */
	int ret=0;
	if(0==(ret=access(profile.get_log_path().c_str(),F_OK)))
	{
		if(0==(ret=access(profile.get_log_path().c_str(),R_OK|W_OK)))
		{
			xmlKeepBlanksDefault(0);
			p_doc=xmlReadFile(profile.get_log_path().c_str(),
						"UTF-8",
						XML_PARSE_RECOVER);	
			p_root_node=xmlDocGetRootElement(p_doc);
		}
		else
		{
			throw Error(errno,E_LOGFPERMDENY);
		}
	}
	else
	{
		int fd;
		mode_t mode=S_IRUSR|S_IWUSR;
		if(-1!=(fd=creat(profile.get_log_path().c_str(),mode)))
		{
			close(fd);
			xmlKeepBlanksDefault(0);
			p_doc=xmlNewDoc(BAD_CAST"1.0");
			p_root_node=xmlNewNode(0,BAD_CAST LOG_TAB_NAME);
			xmlNewProp(p_root_node,
				BAD_CAST"version",BAD_CAST"1.0");
			xmlDocSetRootElement(p_doc,p_root_node);
		}
		else
		{
			throw Error(errno,E_LOGFCREATERR);
		}
	}
	/*
	int fd=open(profile.get_log_path().c_str(),O_CREAT|O_EXCL,S_IRUSR|S_IWUSR);
	if(-1==fd)
	{
		//如果存在日志文件则直接打开
		xmlKeepBlanksDefault(0);
		p_doc=xmlReadFile(profile.get_log_path().c_str(),
						"UTF-8",XML_PARSE_RECOVER);	
		p_root_node=xmlDocGetRootElement(p_doc);
	}
	else
	{
		//如果不存在日志文件则创建文件后关闭文件，由libxml2打开
		close(fd);

		xmlKeepBlanksDefault(0);
		p_doc=xmlNewDoc(BAD_CAST"1.0");
		p_root_node=xmlNewNode(0,BAD_CAST"isync");
		xmlNewProp(p_root_node,
				BAD_CAST "profile",BAD_CAST profile.get_profile_name().c_str());
		xmlDocSetRootElement(p_doc,p_root_node);
	}
	*/
}
catch(Error& error)
{
	error.print_error();
	exit(error.get_error_id());
}

XmlLogger::~XmlLogger()
{
	save_xml();
	xmlFreeDoc(p_doc);
	xmlCleanupParser();
	xmlMemoryDump();
}

void
XmlLogger::log_create(string abs_path)
{
	xmlNodePtr p_ievent_node=xmlNewNode(0,BAD_CAST EVENT_TAB_NAME);
	xmlSetProp(p_ievent_node,BAD_CAST "type",BAD_CAST "create");
	//使用log_time_prop函数记录事件的时间属性
	log_time_prop(p_ievent_node);
	//记录路径
	xmlNodeSetContent(p_ievent_node,BAD_CAST abs_path.c_str());
	xmlAddChild(p_root_node,p_ievent_node);
	save_xml();
}

void
XmlLogger::log_delete(string abs_path)
{
	xmlNodePtr p_ievent_node=xmlNewNode(0,BAD_CAST EVENT_TAB_NAME);
	xmlSetProp(p_ievent_node,BAD_CAST "type",BAD_CAST "delete");
	//使用log_time_prop函数记录事件的时间属性
	log_time_prop(p_ievent_node);
	//记录路径
	xmlNodeSetContent(p_ievent_node,BAD_CAST abs_path.c_str());
	xmlAddChild(p_root_node,p_ievent_node);
	save_xml();
}

void
XmlLogger::log_modify(string abs_path)
{
	xmlNodePtr p_ievent_node=xmlNewNode(0,BAD_CAST EVENT_TAB_NAME);
	xmlSetProp(p_ievent_node,BAD_CAST "type",BAD_CAST "modify");
	//使用log_time_prop函数记录事件的时间属性
	log_time_prop(p_ievent_node);
	//记录路径
	xmlNodeSetContent(p_ievent_node,BAD_CAST abs_path.c_str());
	xmlAddChild(p_root_node,p_ievent_node);
	save_xml();
}

void
XmlLogger::log_moved_from(string abs_path,uint32_t cookie)
{
	xmlNodePtr p_ievent_node=xmlNewNode(0,BAD_CAST EVENT_TAB_NAME);
	xmlSetProp(p_ievent_node,BAD_CAST "type",BAD_CAST "moved_from");
	xmlSetProp(p_ievent_node,BAD_CAST "cookie",
			BAD_CAST Conv.conv<string>(cookie).c_str());
	//使用log_time_prop函数记录事件的时间属性
	log_time_prop(p_ievent_node);
	//记录路径
	xmlNodeSetContent(p_ievent_node,BAD_CAST abs_path.c_str());
	xmlAddChild(p_root_node,p_ievent_node);
	save_xml();
}

void
XmlLogger::log_moved_to(string abs_path,uint32_t cookie)
{
	xmlNodePtr p_ievent_node=xmlNewNode(0,BAD_CAST EVENT_TAB_NAME);
	xmlSetProp(p_ievent_node,BAD_CAST "type",BAD_CAST "moved_to");
	xmlSetProp(p_ievent_node,BAD_CAST "cookie",
			BAD_CAST Conv.conv<string>(cookie).c_str());
	//使用log_time_prop函数记录事件的时间属性
	log_time_prop(p_ievent_node);
	//记录路径
	xmlNodeSetContent(p_ievent_node,BAD_CAST abs_path.c_str());
	xmlAddChild(p_root_node,p_ievent_node);
	save_xml();
}

void
XmlLogger::log_move_inside(string src_path,
				string dest_path,
				uint32_t cookie)
{
	//删除之前必定记录过的moved_from事件
	unlog_moved_from(src_path,cookie);
	//记录新的moved_inside事件
	xmlNodePtr p_ievent_node=xmlNewNode(0,BAD_CAST EVENT_TAB_NAME);
	xmlSetProp(p_ievent_node,BAD_CAST "type",BAD_CAST "move_inside");
	//使用log_time_prop函数记录事件的时间属性
	log_time_prop(p_ievent_node);
	//设置src节点
	xmlNodePtr p_src_node=xmlNewNode(0,BAD_CAST "src");
	xmlNodeSetContent(p_src_node,BAD_CAST src_path.c_str());
	xmlAddChild(p_ievent_node,p_src_node);
	//设置dest节点
	xmlNodePtr p_dest_node=xmlNewNode(0,BAD_CAST "dest");
	xmlNodeSetContent(p_dest_node,BAD_CAST dest_path.c_str());
	xmlAddChild(p_ievent_node,p_dest_node);
	//将ievent_node设置为root_node的子节点
	xmlAddChild(p_root_node,p_ievent_node);
	//保存
	save_xml();
}

void
XmlLogger::unlog_moved_from(string abs_path,uint32_t cookie)
{
	p_cur_node=p_root_node->xmlChildrenNode;
	while(0!=p_cur_node)
	{
		xmlChar *p_type=xmlGetProp(p_cur_node,BAD_CAST "type");
		if(!xmlStrcmp(p_type,BAD_CAST "moved_from"))
		{
		//如果是moved_from的事件则判断cookie
			xmlChar *p_cookie=xmlGetProp(p_cur_node,BAD_CAST "cookie");
			if(!xmlStrcmp(p_cookie,
				BAD_CAST Conv.conv<string>(cookie).c_str()))
			{
			//如果cookie相同则继续判断路径
				xmlChar *p_path=xmlNodeGetContent(p_cur_node);
				if(!xmlStrcmp(p_path,BAD_CAST abs_path.c_str()))
				{
				//如果路径相同则删除事件
					xmlUnlinkNode(p_cur_node);
					xmlFreeNode(p_cur_node);
					save_xml();
					return;
				}
			}
		}
		p_cur_node=p_cur_node->next;
	}
}

void
XmlLogger::log_time_prop(xmlNodePtr p_node)
{
	//获取当前精确时间
	timeval time;
	if(gettimeofday(&time,0))
		perror("gettimeofday");
	//产生用户可识别的时间
	struct tm *p_tm=localtime(&(time.tv_sec));
	string year=Conv.conv<string>(p_tm->tm_year+1900);
	string month=Conv.conv<string>(p_tm->tm_mon+1);
	string day=Conv.conv<string>(p_tm->tm_mday);
	string hour=Conv.conv<string>(p_tm->tm_hour);
	string min=Conv.conv<string>(p_tm->tm_min);
	string sec=Conv.conv<string>(p_tm->tm_sec);
	string time_str=string(year+"-"+month+"-"+day+" "+hour+":"+min+":"+sec);
	//产生标准时间，秒和微秒
	string sec_str=Conv.conv<string>(time.tv_sec);
	string usec_str=Conv.conv<string>(time.tv_usec);
	//用获取的时间插入节点属性time、sec和usec
	xmlSetProp(p_node,BAD_CAST "time",BAD_CAST time_str.c_str());
	xmlSetProp(p_node,BAD_CAST "sec",BAD_CAST sec_str.c_str());
	xmlSetProp(p_node,BAD_CAST "usec",BAD_CAST usec_str.c_str());
}

void
XmlLogger::save_xml()
{
	xmlSaveFormatFileEnc(profile.get_log_path().c_str(),
				p_doc,"UTF-8",1);
}
